#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "cuci.h"

/**
* @brief 获取setion的section-type或者option的value
* @param[in] acc_path:需要被访问的位置的路径，<config>.<section>[.<option>]，并且支持节点类型”数组的方式访问，如network.@interface[1]
* @param[out] out_str:读取到的字符串存放位置
* @param[in] str_size_max:读取到字符串存放的缓冲区大小
* @retval UCI_OK:成功 非UCI_OK:失败
*/
int cuci_get(const char *acc_path, char *out_str, size_t str_size_max)
{
    struct uci_context *ctx;
    struct uci_ptr ptr;
    struct uci_element *e = NULL;
    char *path;
    int ret = UCI_ERR_NOTFOUND;
    if (acc_path == NULL) {
        return UCI_ERR_INVAL;
    }
    ctx = uci_alloc_context();
    if (!ctx)
    {
        return UCI_ERR_MEM;
    }
    path = strdup(acc_path);
    if (path == NULL) {
        return UCI_ERR_MEM;
    }
    if ( (uci_lookup_ptr(ctx, &ptr, path, true) != UCI_OK) ||
         !(ptr.flags & UCI_LOOKUP_COMPLETE) )
    {
        uci_free_context(ctx);
        free(path);
        return UCI_ERR_NOTFOUND;
    }

    e = ptr.last;
    switch (e->type)
    {
        case UCI_TYPE_SECTION:
            if (out_str != NULL) {
                strncpy(out_str, uci_to_section(e)->type, str_size_max);
            }
            ret = UCI_OK;
            break;
        case UCI_TYPE_OPTION:
            if (ptr.o->type == UCI_TYPE_STRING)
            {
                if (out_str != NULL) {
                    strncpy(out_str, ptr.o->v.string, str_size_max);
                }
                ret = UCI_OK;
            }
            else {//对于list要用cuci_foreach_list()/cuci_find_str_list()
                ret = UCI_ERR_NOTFOUND;
            }
            break;
        default:
            ret = UCI_ERR_NOTFOUND;
            break;
    }
    uci_free_context(ctx);
    free(path);
    return ret;
}

/**
* @brief 设置setion的section-type或者option的value
* @param[in] acc_path:需要被访问的位置的路径，<config>.<section>[.<option>]，并且支持节点类型”数组的方式访问，如network.@interface[1]
* @param[in] set_str:需要写入的字符串
* @note1 如果对应位置为空，则会创建一个新的section或者option；
* @note2 如果对应位置为list，则会替换成option
* @note3 当对应位置有相同值的时候，不会重复写入
* @retval UCI_OK:成功 非UCI_OK:失败
*/
int cuci_set(const char *acc_path, const char *set_str)
{
    struct uci_context *ctx;
    struct uci_ptr ptr;
    struct uci_element *e = NULL;
    char *path;
    int ret = UCI_ERR_NOTFOUND;
    if ( (acc_path == NULL) || (set_str == NULL) ) {
        return UCI_ERR_INVAL;
    }

    ctx = uci_alloc_context();
    if (!ctx)
    {
        return UCI_ERR_MEM;
    }

    path = strdup(acc_path);
    ret = uci_lookup_ptr(ctx, &ptr, path, true);
    if (ret != UCI_OK)
    {
        uci_free_context(ctx);
        free(path);
        return ret;
    }
    //判断是否需要写入
    if (ptr.flags & UCI_LOOKUP_COMPLETE)
    {
        bool isNeedSet = true;
        e = ptr.last;
        switch (e->type)
        {
            case UCI_TYPE_SECTION:
                if (strcmp(uci_to_section(e)->type, set_str) == 0) {
                    isNeedSet = false;
                }
                break;
            case UCI_TYPE_OPTION:
                if (ptr.o->type == UCI_TYPE_STRING)
                {
                    if (strcmp(ptr.o->v.string, set_str) == 0) {
                        isNeedSet = false;
                    }
                }
                else {//相当于将list直接替换成option
                    isNeedSet = true;
                }
                break;
            default:
                break;
        }
        if (!isNeedSet) {
            uci_free_context(ctx);
            free(path);
            return UCI_OK;
        }
    }
    ptr.value = set_str;

    ret = uci_set(ctx, &ptr);
    if (ret != UCI_OK)
    {
        uci_free_context(ctx);
        free(path);
        return ret;
    }

    ret = uci_commit(ctx, &ptr.p, false);

    uci_free_context(ctx);
    free(path);
    return ret;
}

/**
* @brief 遍历list
* @param[in] acc_path:需要被访问的位置的路径，<config>.<section>[.<option>]，并且支持节点类型”数组的方式访问，如network.@interface[1]
* @param[in] list_cb:当遍历到字符串后，将通过这个回调函数对字符串进行相应处理
* @param[in] param1_cb:传输给回调函数list_cb的参数
* @retval UCI_OK:成功 非UCI_OK:失败
*/
int cuci_foreach_list(const char *acc_path, void (*list_cb)(char *list_value, void *ret), void *param1_cb)
{
    struct uci_context *ctx;
    struct uci_ptr ptr;
    struct uci_element *e = NULL;
    char *path;
    int ret = UCI_ERR_NOTFOUND;
    if ( (acc_path == NULL) || (list_cb == NULL) ) {
        return UCI_ERR_INVAL;
    }
    ctx = uci_alloc_context();
    if (!ctx)
    {
        return UCI_ERR_MEM;
    }
    path = strdup(acc_path);
    if (path == NULL) {
        return UCI_ERR_MEM;
    }
    if ( (uci_lookup_ptr(ctx, &ptr, path, true) != UCI_OK) ||
         !(ptr.flags & UCI_LOOKUP_COMPLETE) ||
         (ptr.last->type != UCI_TYPE_OPTION) )
    {
        uci_free_context(ctx);
        free(path);
        return UCI_ERR_NOTFOUND;
    }

    if (ptr.o->type == UCI_TYPE_STRING)
    {//对于list要用cuci_get()/
        ret = UCI_ERR_NOTFOUND;
    }
    else {
        uci_foreach_element(&ptr.o->v.list, e)
        {
            list_cb(e->name, param1_cb);
        }
        ret = UCI_OK;
    }
    uci_free_context(ctx);
    free(path);
    return ret;
}

/**
* @brief 判断list中是否有对应的字符串
* @param[in] acc_path:需要被访问的位置的路径，<config>.<section>.<option>，并且支持节点类型”数组的方式访问，如firewall.@zone[1].network
* @param[in] set_str:需要写入的字符串
* @retval true:找到 false：未找到
*/
bool cuci_is_list_have_str(const char *acc_path, const char *find_str)
{
    struct uci_context *ctx;
    struct uci_ptr ptr;
    struct uci_element *e = NULL;
    char *path;
    bool ret = false;

    if ( (acc_path == NULL) || (find_str == NULL) ) {
        return false;
    }
    ctx = uci_alloc_context();
    if (!ctx)
    {
        return false;
    }
    path = strdup(acc_path);
    if (path == NULL) {
        return false;
    }
    if ( (uci_lookup_ptr(ctx, &ptr, path, true) != UCI_OK) ||
         !(ptr.flags & UCI_LOOKUP_COMPLETE) ||
         (ptr.last->type != UCI_TYPE_OPTION) )
    {
        uci_free_context(ctx);
        free(path);
        return false;
    }

    if (ptr.o->type == UCI_TYPE_STRING)
    {//对于list要用cuci_get()/
        ret = false;
    }
    else {
        uci_foreach_element(&ptr.o->v.list, e)
        {
            if (strcmp(e->name, find_str) == 0) {
                ret = true;
            }
        }
    }
    uci_free_context(ctx);
    free(path);
    return ret;
}

/**
* @brief 往list中添加列表值
* @param[in] acc_path:需要被访问的位置的路径，<config>.<section>.<option>，并且支持节点类型”数组的方式访问，如firewall.@zone[1].network
* @param[in] add_str:需要增加的字符串
* @note1 如果对应位置为空，则会创建一个新的list；
* @note2 如果对应位置为option，则会替换成list，并且在list中的首个列表值为原option中的值
* @note3 当对应位置有相同值的时候，不会重复写入
* @retval UCI_OK:成功 非UCI_OK:失败
*/
int cuci_add_list(const char *acc_path, char *add_str)
{
    struct uci_context *ctx;
    struct uci_ptr ptr;
    struct uci_element *e = NULL;
    char *path;
    int ret = UCI_ERR_NOTFOUND;
    if ( (acc_path == NULL) || (add_str == NULL) ) {
        return UCI_ERR_INVAL;
    }
    ctx = uci_alloc_context();
    if (!ctx)
    {
        return UCI_ERR_MEM;
    }
    path = strdup(acc_path);
    if (path == NULL) {
        return UCI_ERR_MEM;
    }
    if (uci_lookup_ptr(ctx, &ptr, path, true) != UCI_OK)
    {
        uci_free_context(ctx);
        free(path);
        return UCI_ERR_NOTFOUND;
    }

    //判断是否需要写入
    if ((ptr.flags & UCI_LOOKUP_COMPLETE) && (ptr.last->type == UCI_TYPE_OPTION))
    {
        bool isNeedSet = true;


        if (ptr.o->type == UCI_TYPE_LIST)
        {
            uci_foreach_element(&ptr.o->v.list, e)
            {
                if (strcmp(e->name, add_str) == 0) {
                    isNeedSet = false;
                }
            }
        }

        if (!isNeedSet) {
            printf("list string is exist\n");
            uci_free_context(ctx);
            free(path);
            return UCI_OK;
        }
    }
    ptr.value = add_str;

    ret = uci_add_list(ctx, &ptr);
    if (ret != UCI_OK)
    {
        uci_free_context(ctx);
        free(path);
        return ret;
    }

    ret = uci_commit(ctx, &ptr.p, false);

    uci_free_context(ctx);
    free(path);
    return ret;
}